/*********************************************************************************
Description: 
Author: SA12226114(Xin Zhu), SA12226139(Dan Su)
Date: 2012-11 

Copyright (c) 2012-2022 School of Software Engineering of USTC, All rights reserved.

**********************************************************************************/



#include "dbmemcache.h"


void Handle(void *sockfd);


typedef struct kv_pair
{
    char key[KEY_LEN];
    char value[VALUE_LEN];
    LILY_BOOLEAN used;
}kv_pair_t;

kv_pair_t *g_kv_table = NULL;


/*****************************************************************
Function Name: main
Description:
Parameter:  

@argc: argument count
@argv: argument vector

Author: 
Date:  
******************************************************************/
int main(int argc, char** argv)
{
    int sockfd_router, listenfd, connfd, epfd, nfds, index, status;
    struct sockaddr_in servaddr;
    void *db = NULL;
    char sendline[CMDLINE_LEN] = {0}, cmd[CMD_LEN], key[KEY_LEN], value[VALUE_LEN], retval[VALUE_LEN] = {0}, local_ip[32];
    struct epoll_event ev, events[20];
    
    /* check whether argument count is right */
    if(argc != 2)
    {
        printf("usage: ./dbmemcache <router's ip address>\n");
        
        return 0;
    }

    /* when memcache start, it needs to notify router. */
    sw_connect_server(argv[1], &sockfd_router);
    sw_get_host_ip("memcache1", local_ip);
    sprintf(sendline, "add_memcache_node %s", local_ip);
    /* share local host's ip address with router */    
    sw_send(sockfd_router, sendline, strlen(sendline), 0);

    /* ----------------------------------------------------------------------------------- */
    /* initialize thread pool, we need to destroy thread pool when we exit program. */
    pool_init(10);

    /* generate a new socket for server end */
    listenfd = sw_create_socket(PF_INET, SOCK_STREAM, 0);

    /* initialize structure sockaddr_in with protocol family, ip, port number */
    sw_init_socket2(&servaddr, sizeof(servaddr));

    /* bind socket and socket id */
    sw_bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));

    /* the second parameter is max number of client connection */
    sw_listen(listenfd, 10);
    
    /* generate a specific epoll file descriptor for accepting */
    epfd = epoll_create(256);

    /* make the socket non blocking */
    sw_make_socket_non_blocking(listenfd);
    
    ev.data.fd = listenfd;                  /* file descriptor needs to be watched */
    ev.events = EPOLLIN|EPOLLET|EPOLLOUT;   /* event type needs to be handled */

    /* register epoll event */
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    /* initialize global key and value pairs buffer space */
    g_kv_table = malloc(sizeof(kv_pair_t)*KV_TABLE_LENGTH);
    if(!g_kv_table)
    {
        printf("g_kv_table is null\n");
        
        return;
    }

    memset(g_kv_table, 0, sizeof(kv_pair_t)*KV_TABLE_LENGTH);
    
    /* The event loop */
    while(1)
    {
        struct sockaddr in_addr;
        socklen_t in_len;
        char hbuf[200], sbuf[200];
        in_len = sizeof(in_addr);
        
        /* epoll_wait blocks until events are available */
        nfds = epoll_wait(epfd, events, 500, 500);

        /* handle event */
        for(index = 0; index < nfds; ++index)
        {
            /* We have a notification on the listening socket, which means one or more new incoming connections. */
            if(events[index].data.fd == listenfd)
            {                
                /* accept new connection from client */
                //connfd = sw_accept(listenfd);
                if((connfd = accept(listenfd, &in_addr, &in_len)) == -1)
                {
                    printf("accept socket error: %s(errno: %d)", strerror(errno), errno);

                    return 0;
                }
                
                status = getnameinfo(&in_addr, in_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
                if(status == 0)
                    debug_print("Accepted connection on descriptor %d (host ip=%s, port=%s)\n", connfd, hbuf, sbuf);
                
                pool_add_job(Handle, &connfd);
            
                /* make the incoming socket non-blocking and add it to the list of fds to monitor. */
                sw_make_socket_non_blocking(connfd);

                ev.data.fd = connfd;
                ev.events = EPOLLIN|EPOLLET;
                
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }
            else if(events[index].events & EPOLLIN) /* It is existed connection and there are data incoming. */
            {
                pool_add_job(Handle, &(events[index].data.fd));
            }
            
        }
    }

}


/*****************************************************************
Function Name: main
Description:
Parameter:  

@argc: argument count
@argv: argument vector

Author: 
Date:  
******************************************************************/
void Handle(void *sockfd)
{
    int sockfd_client, sockfd_dbserver, connfd, epfd, nfds, index = 0;
    struct sockaddr_in servaddr;
    struct sockaddr_in client_addr;
    char cmdline[CMDLINE_LEN], cmd[CMD_LEN], key[KEY_LEN], value[VALUE_LEN], retval[VALUE_LEN] = {0}, recvline[MAXLINE];
    struct epoll_event ev, events[20];
    LILY_BOOLEAN iswritten = LILY_FALSE, isreaded = LILY_FALSE, isremoved = LILY_FALSE;

    sockfd_client = (*(int *)sockfd);
    util_init(cmdline, CMDLINE_LEN);
    while((sw_recv(sockfd_client, cmdline, CMDLINE_LEN, 0)) > 0)
    {
        
        util_init(cmd, CMD_LEN);
        util_init(key, KEY_LEN);
        util_init(value, VALUE_LEN);
        
        /* put messages into buffer */
        printf("received message is : %s\n", cmdline);

        util_parse(cmdline, CMDLINE_LEN, cmd, key, value);
        util_memset(cmd, CMD_LEN);
        util_memset(key, KEY_LEN);
        util_memset(value, VALUE_LEN);

        sw_connect_server("192.168.10.94", &sockfd_dbserver);

        if(strncmp(cmd, "open", sizeof("open")) == 0)                   /* create database */
        {
            //db_create(&g_db, key);
            //sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
            //sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);
            //sw_send(sockfd_client, recvline, MAXLINE, 0);
        }
        else if(strncmp(cmd, "set", sizeof("set")) == 0)                /* store records */
        {

            for(; index < KV_TABLE_LENGTH; index++)
            {
                if(strncmp(g_kv_table[index].key, key, strlen(key)) == 0)
                {
                    /*  */
                    //sw_send(sockfd_client, g_kv_table[index].value, VALUE_LEN, 0);
                    printf("set-----strncmp(g_kv_table[index].key, key, strlen(key)) == 0\n");

                    strncpy(g_kv_table[index].key, key, KEY_LEN);
                    strncpy(g_kv_table[index].value, value, VALUE_LEN);
                    g_kv_table[index].used = LILY_TRUE;

                    sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
                    sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);
                    sw_send(sockfd_client, recvline, MAXLINE, 0);

                    iswritten = LILY_TRUE;

                    break;
                }
            }    


            for(index = 0; (index < KV_TABLE_LENGTH && (!iswritten)); index++)
            {

                if(g_kv_table[index].used == LILY_FALSE)
                {
                    /*  */
                    printf("set-----strncmp(g_kv_table[index].key, key, strlen(key)) != 0\n");
                    
                    strncpy(g_kv_table[index].key, key, KEY_LEN);
                    strncpy(g_kv_table[index].value, value, VALUE_LEN);
                    g_kv_table[index].used = LILY_TRUE;
                    iswritten = LILY_TRUE;
                    
                    sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
                    sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);
                    sw_send(sockfd_client, recvline, MAXLINE, 0);

                    break;
                }

            }

            
        }
        else if(strncmp(cmd, "get", sizeof("get")) == 0)                /* retrieve records */
        {            
            for(index = 0; index < KV_TABLE_LENGTH; index++)
            {
                if(strncmp(g_kv_table[index].key, key, strlen(key)) == 0)
                {
                    /* send value directly from memcache kv buffer space to client */
                    printf("get-----strncmp(g_kv_table[index].key, key, strlen(key)) == 0\n");
                    strncpy(recvline, g_kv_table[index].value, VALUE_LEN);
                    sw_send(sockfd_client, recvline, MAXLINE, 0);

                    isreaded = LILY_TRUE;
                    
                    break;
                }
            }

            for(index = 0; (index < KV_TABLE_LENGTH && (!isreaded)); index++)
            {
                if(g_kv_table[index].used == LILY_FALSE)
                {
                    /* first, get value from dbserver, then send it to client. */
                    printf("get-----strncmp(g_kv_table[index].key, key, strlen(key)) != 0\n");
                    sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
                    sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);

                    util_init(cmd, CMD_LEN);
                    util_init(key, KEY_LEN);
                    util_init(value, VALUE_LEN);
                    util_parse(recvline, CMDLINE_LEN, cmd, key, value);
                    util_memset(cmd, CMD_LEN);
                    util_memset(key, KEY_LEN);
                    util_memset(value, VALUE_LEN);
                    
                    strncpy(g_kv_table[index].key, key, KEY_LEN);
                    strncpy(g_kv_table[index].value, value, VALUE_LEN);
                    g_kv_table[index].used = LILY_TRUE;
                    isreaded = LILY_TRUE;

                    printf("recvline=%s\n", recvline);
                    sw_send(sockfd_client, recvline, MAXLINE, 0);
                    
                    break;

                }
            }
            
        }
        else if(strncmp(cmd, "remove", sizeof("remove")) == 0)   
        {
            printf("enter remove-----\n");
            for(index = 0; index < KV_TABLE_LENGTH; index++)
            {
                if(strncmp(g_kv_table[index].key, key, strlen(key)) == 0)
                {
                    /* send value directly from memcache kv buffer space to client */
                    printf("remove-----strncmp(g_kv_table[index].key, key, strlen(key)) == 0\n");

                    memset(g_kv_table[index].key, 0, KEY_LEN);
                    memset(g_kv_table[index].value, 0, VALUE_LEN);

                    sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
                    sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);
                    sw_send(sockfd_client, recvline, MAXLINE, 0);

                    isremoved = LILY_TRUE;
                    
                    break;
                }
            }

            if(!isremoved)
            {
                printf("remove-----strncmp(g_kv_table[index].key, key, strlen(key)) != 0\n");
                sw_send(sockfd_dbserver, cmdline, CMDLINE_LEN, 0);
                sw_recv(sockfd_dbserver, recvline, MAXLINE, 0);
                
                printf("recvline=%s\n", recvline);
                sw_send(sockfd_client, recvline, MAXLINE, 0);

            }

        }


    }

}



